package cytargetlinker.conversion;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.bridgedb.DataSource;
import org.bridgedb.IDMapperException;
import org.bridgedb.IDMapperStack;
import org.bridgedb.Xref;
import org.bridgedb.bio.BioDataSource;

import cytargetlinker.conversion.ArgsParser.AFilesIn;
import cytargetlinker.conversion.ArgsParser.AFilesOut;
import cytargetlinker.conversion.ArgsParser.AHelp;
import cytargetlinker.conversion.ArgsParser.GraphBuilder;
import cytargetlinker.conversion.graph.Graph;
import cytargetlinker.conversion.graph.Graph.Edge;
import cytargetlinker.conversion.graph.Graph.Node;

/**
 * Converts miRNA target text files from TarBase to a XGMML or GML network.
 * 
 * The input files for this script can be downloaded here:
 * 
 * 
 * @author Thomas, Pooja, Naime, Tina
 */
public class TarBase {
	
	private final static Logger log = Logger.getLogger(TarBase.class.getName());
	
	
	public static void main(String argv[]) throws Exception {
		
		Args pargs = ArgsParser.parse(argv, Args.class);
		new TarBaseConverter(pargs);
		
	}
	
	public static class TarBaseConverter {
		
		private IDMapperStack gdb;
		private Graph graph;
		private Map<String, Integer> index;
		private Map<String, List<String>> edges;
		
		private String systemCodeIn = "EnHs";
		private String systemCodeOut = "L";
		
		public TarBaseConverter(Args pargs) throws Exception {
			if(init()) {
				log.info("conversion of TarBase file started ...\n");
				ArgsParser.convertAndWrite(pargs, pargs, new GraphBuilder() {
					public Graph buildGraph(File in) throws Exception {
						return importMiRecords(in);
					}
				});
				log.info("conversion of TarBase file finalized ...\n");
			} else {
				log.severe("BridgeDb IDMapper could not be initialized.\n");
			}
		}
		
		private boolean init() {
			BioDataSource.init();
			try {
				Class.forName("org.bridgedb.rdb.IDMapperRdb");
			}
			catch (ClassNotFoundException ex) {
				System.out.println("Bridgedb class not found");
				return false;
			}
			File f = new File ("conversion/resources/Hs_Derby_20110601.bridge");
			if (f.exists()) { 
				try {
					gdb = new IDMapperStack();
					gdb.addIDMapper("idmapper-pgdb:" + f.getAbsolutePath());
					gdb.setTransitive(true);
				} catch (IDMapperException e) {
					System.out.println("mapper exception");
					return false;
				}
			} else {
				System.out.println("file not found");
				return false;
			}
			return true;
		}

		private List<String> genesNotFound;
		private List<String> foundConnections;
		
		public Graph importMiRecords(File input) throws IOException {
			genesNotFound = new ArrayList<String>();
			foundConnections = new ArrayList<String>();
			
			BufferedReader br = new BufferedReader(new FileReader(input));
			String[] header = br.readLine().split("\t");
			index = new HashMap<String, Integer>();
			for(int i = 0; i < header.length; i++) {
				index.put(header[i], i);
			}
			graph = new Graph();
			setNetworkAttributes(input);
			
			//Load each line into memory
			String line = null;
			List<String[]> rows = new ArrayList<String[]>();
			while((line = br.readLine()) != null) {
				String [] str = line.split("\t", header.length);
				if(str[index.get("Organism")].equals("Human")) {
					rows.add(line.split("\t", header.length));
				}
			}
			
			edges = new HashMap<String, List<String>>();
			for(String[] r : rows) {
				
				String id = r[index.get("Ensembl")];
				
				String newId = mapId(id);
				if(newId != null) {
					Node n = graph.addNode(newId);
					n.setAttribute("EntrezGene", newId);
					n.setAttribute("label", r[index.get("Gene")]);
					n.setAttribute("name", r[index.get("Gene")]);
					n.setAttribute("Organism", r[index.get("Organism")]);
					n.setAttribute("HGNC_Symbol", r[index.get("HGNC_Symbol")]);
					n.setAttribute("Isoform", r[index.get("Isoform")]);
					n.setAttribute("Chromosomal_location", r[index.get("Chr_loc")]);
					n.setAttribute("HGNC_ID", r[index.get("HGNC_ID")]);
					n.setAttribute("SwissProt", r[index.get("SwissProt")]);
							
					
					String mirna = getMiRNA(r[index.get("miRNA")],r[index.get("Organism")]);

					Node mirnaNode = graph.addNode(mirna);
					mirnaNode.setAttribute("miRNA", mirna);
					mirnaNode.setAttribute("label", mirna);
					mirnaNode.setAttribute("name", mirna);
					mirnaNode.setAttribute("Organism", r[index.get("Organism")]);
					mirnaNode.setAttribute("Pathology_or_Event",r[index.get("Pathology_or_Event")]);
					mirnaNode.setAttribute("miRNA_seq", r[index.get("miRNA_seq")]);
					mirnaNode.setAttribute("Seq_location", r[index.get("Seq_location")]);
					mirnaNode.setAttribute("Target_seq", r[index.get("Target_seq")]);
						
					addEdge(newId, mirna, r);
				} else {
					String str = id + "\t" + r[index.get("Gene")];
					if(!genesNotFound.contains(str)) {
						genesNotFound.add(str);
					}	
				}
			}
			
			printResults(false);
			
			return graph;
		}
		
		private void printResults(boolean printDetails) {
			
			System.out.println("no mapping for " + genesNotFound.size() + " genes.");
			System.out.println("found " + foundConnections.size()+ " miRNA-gene interactions.\n");
			
			if(printDetails) {
				System.out.println("------------------------\n");
				System.out.println("GENES NOT FOUND " + genesNotFound.size());
				for(String str : genesNotFound) {
					System.out.println(str);
				}
			
				System.out.println("FOUND CONNECTIONS " + foundConnections.size());
				for(String str : foundConnections) {
					System.out.println(str);
				}	
				
				System.out.println("\n------------------------");
				System.out.println("no mapping for " + genesNotFound.size() + " genes.");
				System.out.println("found " + foundConnections.size()+ " miRNA-gene interactions.\n");
			}
		}
		
		private void setNetworkAttributes(File input) {
			graph.setTitle("TarBase");
			graph.setAttribute(CommonAttributes.DATABASE.getName(), "TarBase");
			graph.setAttribute(CommonAttributes.SOURCE_FILE.getName(), input.getName());
			graph.setAttribute(CommonAttributes.TYPE.getName(), "miRNA targets");
			graph.setAttribute(CommonAttributes.SOURCE_FILE.getName(), input.getName());
			graph.setAttribute(CommonAttributes.TARGET_ID_ATTRIBUTE.getName(), "EntrezGene");
			graph.setAttribute(CommonAttributes.TARGET_DATASOURCE.getName(), "EntrezGene");
			graph.setAttribute(CommonAttributes.SOURCE_ID_ATTRIBUTE.getName(), "miRNA");
			graph.setAttribute(CommonAttributes.SOURCE_DATASOURCE.getName(), "");
			graph.setAttribute(CommonAttributes.SOURCE_TYPE.getName(), "mirna");
			graph.setAttribute(CommonAttributes.TARGET_TYPE.getName(), "gene");
		}
		
		private static String getMiRNA(String id, String species) {
			String code = SpeciesCodes.getCode(species);
			if(code != null) return code + "-" + id;
			return id;
		}
		
		private String mapId(String oldId) {
			if(oldId.contains(".")) {
				int pos = oldId.indexOf(".");
				oldId = oldId.substring(0, pos);
			}
			Xref srcRef = new Xref(oldId, DataSource.getBySystemCode(systemCodeIn));
			Set<Xref> result;
			try {
				result = gdb.mapID(srcRef, DataSource.getBySystemCode(systemCodeOut));
				if(!result.isEmpty()) {
					return result.toArray(new Xref[0])[0].getId();
				}
			} catch (IDMapperException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return null;
		}
		
		private Integer countEdges = 0;
		
		private void addEdge(String gene, String mirna, String [] r) {
			
			if(edges.containsKey(gene)) {
				if(!edges.get(gene).contains(mirna)) {
					Edge e = graph.addEdge("" + countEdges, graph.getNode(mirna),graph.getNode(gene));
					e.setAttribute("Data_Type", r[index.get("Data_Type")]);
					e.setAttribute("Support_Type", r[index.get("Support_Type")]);
					e.setAttribute("Organism", r[index.get("Organism")]);
					e.setAttribute("MRE", r[index.get("MRE")]);
					e.setAttribute("S_S_S", r[index.get("S_S_S")]);
					e.setAttribute("I_S", r[index.get("I_S")]);
					e.setAttribute("D_S", r[index.get("D_S")]);
					e.setAttribute("Validation", r[index.get("Validation")]);
					e.setAttribute("Paper", r[index.get("Paper")]);
					e.setAttribute("PMID", r[index.get("PMID")]);
					e.setAttribute("Bibliographic_Notes", r[index.get("Bibliographic_Notes")]);
					e.setAttribute("Cell_Line_Used", r[index.get("Cell_Line_Used")]);
					edges.get(gene).add(mirna);
					foundConnections.add(gene + "\t" + mirna);
					countEdges++;
				}
			} else {
				
				Edge e = graph.addEdge("" + countEdges, graph.getNode(mirna),graph.getNode(gene));
				e.setAttribute("Data_Type", r[index.get("Data_Type")]);
				e.setAttribute("Support_Type", r[index.get("Support_Type")]);
				e.setAttribute("Organism", r[index.get("Organism")]);
				e.setAttribute("MRE", r[index.get("MRE")]);
				e.setAttribute("S_S_S", r[index.get("S_S_S")]);
				e.setAttribute("I_S", r[index.get("I_S")]);
				e.setAttribute("D_S", r[index.get("D_S")]);
				e.setAttribute("Validation", r[index.get("Validation")]);
				e.setAttribute("Paper", r[index.get("Paper")]);
				e.setAttribute("PMID", r[index.get("PMID")]);
				e.setAttribute("Bibliographic_Notes", r[index.get("Bibliographic_Notes")]);
				e.setAttribute("Cell_Line_Used", r[index.get("Cell_Line_Used")]);
				
				List<String> list = new ArrayList<String>();
				list.add(mirna);
				foundConnections.add(gene + "\t" + mirna);
				edges.put(gene, list);
				countEdges++;
			}
		}
	}
	
	private interface Args extends AHelp, AFilesIn, AFilesOut {}
}
